#include "AudioDevice.h"
#include <QtMultimedia/QAudioDeviceInfo>
#include <QtMultimedia/QAudioInput>
#include <QtCore/qendian.h>

namespace ham
{
	AudioDevice::AudioDevice(const QAudioFormat& format, QObject* parent)
		: QIODevice(parent), m_format(format),
		m_maxAmplitude(0), m_level(0)
	{ 
		switch (m_format.sampleSize()) {
		case 8:
			switch (m_format.sampleType()) {
			case QAudioFormat::UnSignedInt:
				m_maxAmplitude = 255;
				break;
			case QAudioFormat::SignedInt:
				m_maxAmplitude = 127;
				break;
			default:
				break;
			}
			break;
		case 16:
			switch (m_format.sampleType()) {
			case QAudioFormat::UnSignedInt:
				m_maxAmplitude = 65535;
				break;
			case QAudioFormat::SignedInt:
				m_maxAmplitude = 32767;
				break;
			default:
				break;
			}
			break;
		case 32:
			switch (m_format.sampleType()) {
			case QAudioFormat::UnSignedInt:
				m_maxAmplitude = 0xffffffff;
				break;
			case QAudioFormat::SignedInt:
				m_maxAmplitude = 0x7fffffff;
				break;
			case QAudioFormat::Float:
				m_maxAmplitude = 0x7fffffff; // Kind of
			default:
				break;
			}
			break;
		default:
			break;
		}
	}

	AudioDevice::~AudioDevice()
	{
	}

	void AudioDevice::start()
	{
		open(QIODevice::WriteOnly);
	}

	void AudioDevice::stop()
	{
		close();
	}

	qint64 AudioDevice::readData(char* data, qint64 maxlen)
	{
		Q_UNUSED(data)
		Q_UNUSED(maxlen)

		return 0;
	}

	qint64 AudioDevice::writeData(const char* data, qint64 len)
	{
		if (m_maxAmplitude) {
			Q_ASSERT(m_format.sampleSize() % 8 == 0);
			const int channelBytes = m_format.sampleSize() / 8;
			const int sampleBytes = m_format.channelCount() * channelBytes;
			Q_ASSERT(len % sampleBytes == 0);
			const int numSamples = len / sampleBytes;

			quint32 maxValue = 0;
			const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data);

			for (int i = 0; i < numSamples; ++i) {
				for (int j = 0; j < m_format.channelCount(); ++j) {
					quint32 value = 0;

					if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
						value = *reinterpret_cast<const quint8*>(ptr);
					} else if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::SignedInt) {
						value = qAbs(*reinterpret_cast<const qint8*>(ptr));
					} else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
						if (m_format.byteOrder() == QAudioFormat::LittleEndian)
							value = qFromLittleEndian<quint16>(ptr);
						else
							value = qFromBigEndian<quint16>(ptr);
					} else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::SignedInt) {
						if (m_format.byteOrder() == QAudioFormat::LittleEndian)
							value = qAbs(qFromLittleEndian<qint16>(ptr));
						else
							value = qAbs(qFromBigEndian<qint16>(ptr));
					} else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
						if (m_format.byteOrder() == QAudioFormat::LittleEndian)
							value = qFromLittleEndian<quint32>(ptr);
						else
							value = qFromBigEndian<quint32>(ptr);
					} else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::SignedInt) {
						if (m_format.byteOrder() == QAudioFormat::LittleEndian)
							value = qAbs(qFromLittleEndian<qint32>(ptr));
						else
							value = qAbs(qFromBigEndian<qint32>(ptr));
					} else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::Float) {
						value = qAbs(*reinterpret_cast<const float*>(ptr) * 0x7fffffff); // assumes 0-1.0
					}

					maxValue = qMax(value, maxValue);
					ptr += channelBytes;
				}
			}

			maxValue = qMin(maxValue, m_maxAmplitude);
			m_level = qreal(maxValue) / m_maxAmplitude;
		}

		emit update();
		return len;
	}
}
